#!/usr/bin/perl -w
#-----------------------------------------------------------------------
#
#  This file is part of 34S.
#
#  34S is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#
#  34S is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with 34S.  If not, see <http://www.gnu.org/licenses/>.
#
#-----------------------------------------------------------------------
#
my $Description = "Convert listing from 'calc XXXX' to *.wp34s format.";
#
# "calc ram" to disassemble RAM contents.
# "calc prog0" to disassemble the backup flash program region.
# "calc prog1" to disassemble the first user flash program region.
# "calc prog2" to disassemble the second user flash program region.
# "calc prog3" to disassemble the third user flash program region.
# "calc xrom" to disassemble the internal user code program.
#-----------------------------------------------------------------------
#
# Language:         Perl script
#
my $SVN_Current_Revision  =  '$Revision: $';
#
#-----------------------------------------------------------------------

use strict;

# ---------------------------------------------------------------------

my $debug = 0;
my $quiet = 1;
my $infile = "-";
my $outfile = "-";
my $first_line = 1;

my $DEFAULT_COMMENT_MARKER = "//";
my $cmt = $DEFAULT_COMMENT_MARKER;
my $no_comments = 0;
my $show_step_num = 1;

# Define the comment format. Only these ones are recognized.
# This is require because there is no specific comment delimiter (other than these) in
# the source files.
my %valid_comments = ( "\\s+Entry\\:"     => "Entry:",
                       "\\s+Error\\:"     => "Error:",
                       "\\s+Internal\\:"  => "Internal:",
                       "\\s+\\->"         => "->",
                    );
my $valid_cmt_list = "";
for my $key (sort keys %valid_comments) {
  $valid_cmt_list .= "'$key' ";
}

# ---------------------------------------------------------------------

my $script_executable = $0;
my $script_name       = "";
if( $script_executable =~ /[\.\/]*(\w+\.pl)/ ) {
  $script_name     = "$1";
}

my $script  = <<SCRIPT;
$script_name  - $Description
SCRIPT

my $usage = <<EOM;

$script
Usage:
   $script_name src_file [-o out_file]

Parameters:
   src_file         Source file in the format output by "calc". If not given, defaults to STDIN.
   -o out_file      Converted source. If not given, defaults to STDOUT.
   -nc              Suppress the comments from being written.
   -h               This help script.

Examples:
  \$ calc xrom > xrom.txt ; $script_name xrom.txt -o xrom.wp34s
  \$ calc xrom | $script_name | tee xrom.wp34s
  - Converts an output generated by "calc" disassembler to WP34S assembler format. Both
    invocations have identical results except the second does not create the intermediate
    "xrom.txt" file.

  \$ $script_name xrom.lst -nc > xrom.wp34s
  - Suppress comments from being written.

Notes:
  1) Comments will be converted to // comments on the line above where they appeared
     in the XROM output. Comments are recognized as starting with one of the following:
        $valid_cmt_list
  2) Blank lines and lines beginning with "//" will be ignored.
  3) The initial "ADDR\\s+OPCODE\\s+MNEMONIC" will be ignored.
  4) This script is primarily for the use of the WP-34s developers. Support will be limited.
EOM

#######################################################################

get_options();

open IN, $infile or die "ERROR: Cannot open input file '$infile' for reading: $!\n";
open OUT, "> $outfile" or die "ERROR: Cannot open output file '$outfile' for writing: $!\n";

my $step = 1;

while(<IN>) {
  next if m<^\s*//>;
  next if /^\s*$/;
  next if /ADDR\s+OPCODE\s+MNEMONIC/;
  chomp; chomp;

  # Detect comments. Detach them if found.
  # We only support those described in the %valid_comments table.
  my $parsed_comment = "";
  foreach my $comment_type (sort keys %valid_comments) {
    if( /${comment_type}(.+)$/ ) {
      $parsed_comment = "${valid_comments{$comment_type}}${1}"; # Recreate the comment.
      s/${comment_type}${1}//; # Remove the comment from the line so we can reprocess it.
      last;
    }
  }

  if( $first_line ) {
    print OUT "${cmt} Original source: $infile\n";
    $first_line = 0;
  }

  # Reprocess the line to parse out only the info needed (or wanted!) by WP34s assembler format.
  if( /[0-9a-f]{4}:\s+([0-9a-f]{4}\s+){1,2}(.+)$/ ) {
    print OUT "${cmt} $parsed_comment\n" if $parsed_comment and not $no_comments;
    if( $show_step_num ) {
      printf OUT "%03d %0s\n", $step, ${2};
      $step++;
    } else {
      print OUT "${2}\n";
    }
  }
}
close IN;
close OUT;


#######################################################################
#######################################################################
#
# Process the command line option list.
#
sub get_options {
  my ($arg);
  while ($arg = shift(@ARGV)) {

    # See if help is asked for
    if( $arg eq '-h' ) {
      print "$usage\n";
      die "\n";
    }

    elsif( ($arg eq "--version") or ($arg eq "-V") ) {
      print "$script\n";
      if( $SVN_Current_Revision =~ /Revision: (.+)\s*\$/ ) {
        print "Version: $1\n";
      }
      die "\n";
    }

    elsif( $arg eq "-d" ) {
      $debug = shift(@ARGV);
    }

    elsif( $arg eq "-v" ) {
      $quiet = 0;
    }

    elsif( $arg eq "-o" ) {
      $outfile = shift(@ARGV);
    }

    elsif( $arg eq "-nc" ) {
      $no_comments = 1;
    }

    elsif( $arg eq "-s" ) {
      $show_step_num = 1;
    }

    elsif( $arg eq "-ns" ) {
      $show_step_num = 0;
    }

    else {
      $infile = $arg;
    }
  }

  #----------------------------------------------

  return;
} # get_options

